﻿using System.ComponentModel;
using Caliburn.Micro;
using NHibernate;
using NHibernate.Linq;
using TimeKeeper.Entities;
using TimeKeeper.Events;
using TimeKeeper.Infrastructure;
using System.Linq;

namespace TimeKeeper.ViewModels
{
    public class ProjectTimesViewModel : Screen, ITab, IHandle<ActualProjectChanged>, IHandle<ProjectTimeChanged>, IHandle<ProjectChanged>
    {
        public override string DisplayName { get { return "Times"; } set { } }

        public Project Project { get; set; }

        private readonly IViewModelFactory<ProjectEditViewModel> _projectEditViewModelViewModelFactory;
        private readonly IViewModelFactory<TimeEditViewModel> _timeEditViewModelViewModelFactory;
        private readonly ISession _session;
        private readonly IEventAggregator _eventAggregator;
        private readonly IEntityFactory _entityFactory;
        private int? _actualProjectId;

        public ProjectTimesViewModel(
            IViewModelFactory<ProjectEditViewModel> projectEditViewModelViewModelFactory,
            IViewModelFactory<TimeEditViewModel> timeEditViewModelViewModelFactory, 
            ISession session, 
            IEventAggregator eventAggregator,
            IEntityFactory entityFactory)
        {
            _projectEditViewModelViewModelFactory = projectEditViewModelViewModelFactory;
            _timeEditViewModelViewModelFactory = timeEditViewModelViewModelFactory;
            _session = session;
            _eventAggregator = eventAggregator;
            _entityFactory = entityFactory;
        }

        public bool CanDeleteProject { get { return Project != null && Project.Id != 0; } }

        public IResult DeleteProject()
        {
            //TODO: Add dialog to configm delete operation.
            _session.DoInTransaction(s => s.Delete(Project));
            _eventAggregator.Publish(new ProjectChanged(Project.Id, ChangeType.Deleted));
            return null;
        }

        public bool CanEditProject { get { return CanDeleteProject; } }

        public IResult EditProject()
        {
            return _projectEditViewModelViewModelFactory.AsDialogResult(x => x.WithProject(Project.Id));
        }

        public bool CanAddTime { get { return CanDeleteProject; } }

        public IResult AddTime()
        {
            return _timeEditViewModelViewModelFactory.AsDialogResult(x => x.WithProject(Project.Id));
        }

        public IResult EditTime(ProjectTime time)
        {
            return _timeEditViewModelViewModelFactory.AsDialogResult(x => x.WithTime(time.Id));
        }

        public void DeleteTime(ProjectTime time)
        {
            //TODO: Add dialog to confirm delete operation.
            _session.DoInTransaction(s => s.Delete(time));
            _eventAggregator.Publish(new ProjectTimeChanged(time.Id, time.Project.Id, ChangeType.Deleted));
        }

        public void Handle(ActualProjectChanged message)
        {
            _actualProjectId = message.Id;

            if (message.Id.HasValue)
                RefreshProject();
            else
                EmptyProject();
        }

        public void Handle(ProjectTimeChanged message)
        {
            if (message.ProjectId != _actualProjectId) return;
            if (message.Type == ChangeType.Modifed)
            {
                _session.DoInTransaction(s => s.Refresh(Project.TimeOnProject.First(x => x.Id == message.TimeId)));
                NotifyOfPropertyChange(() => Project);
            }
            else
            {
                RefreshProject();
            }
        }

        public void Handle(ProjectChanged message)
        {
            if (message.Id != _actualProjectId) return;
            if (message.Type == ChangeType.Deleted)
                EmptyProject();
            else
                RefreshProject();
        }

        private void RefreshProject()
        {
            _session.DoInTransaction(s =>
            {
                Project = s.Query<Project>().First(x => x.Id == _actualProjectId);
                s.Refresh(Project);
                NHibernateUtil.Initialize(Project.TimeOnProject); //Is this correct?
            });
            NotifyOfPropertyChange(() => Project);
            NotifyOfPropertyChange(() => CanAddTime);
            NotifyOfPropertyChange(() => CanDeleteProject);
            NotifyOfPropertyChange(() => CanEditProject);
        }

        private void EmptyProject()
        {
            Project = _entityFactory.Create<Project>();
            NotifyOfPropertyChange(() => Project);
            NotifyOfPropertyChange(() => CanAddTime);
            NotifyOfPropertyChange(() => CanDeleteProject);
            NotifyOfPropertyChange(() => CanEditProject);
        }
    }
}
